package com.ylkj.common.security.des;

import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Created by zhenghao on 2017/8/14.
 */
public class DesUtil implements Security {
    private static Signature signa = null;
    private static String enc_dec_algorithm = "DESede";
    private static String sign_algorithm = "MD5WithRSA";
    private static byte[] codes = new byte[256];

    static {
        int i;
        for(i = 0; i < 256; ++i) {
            codes[i] = -1;
        }

        for(i = 65; i <= 90; ++i) {
            codes[i] = (byte)(i - 65);
        }

        for(i = 97; i <= 122; ++i) {
            codes[i] = (byte)(26 + i - 97);
        }

        for(i = 48; i <= 57; ++i) {
            codes[i] = (byte)(52 + i - 48);
        }

        codes[43] = 62;
        codes[47] = 63;
    }

    public DesUtil() {
    }

    public static String base64Encode(byte[] data) {
        char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
        char[] out = new char[(data.length + 2) / 3 * 4];
        int i = 0;

        for(int index = 0; i < data.length; index += 4) {
            boolean quad = false;
            boolean trip = false;
            int val = 255 & data[i];
            val <<= 8;
            if (i + 1 < data.length) {
                val |= 255 & data[i + 1];
                trip = true;
            }

            val <<= 8;
            if (i + 2 < data.length) {
                val |= 255 & data[i + 2];
                quad = true;
            }

            out[index + 3] = alphabet[quad ? val & 63 : 64];
            val >>= 6;
            out[index + 2] = alphabet[trip ? val & 63 : 64];
            val >>= 6;
            out[index + 1] = alphabet[val & 63];
            val >>= 6;
            out[index + 0] = alphabet[val & 63];
            i += 3;
        }

        return new String(out);
    }

    public static byte[] base64Decode(String dataStr) {
        byte[] data = dataStr.getBytes();
        int len = (data.length + 3) / 4 * 3;
        if (data.length > 0 && data[data.length - 1] == 61) {
            --len;
        }

        if (data.length > 1 && data[data.length - 2] == 61) {
            --len;
        }

        byte[] out = new byte[len];
        int shift = 0;
        int accum = 0;
        int index = 0;

        for(int ix = 0; ix < data.length; ++ix) {
            int value = codes[data[ix] & 255];
            if (value >= 0) {
                accum <<= 6;
                shift += 6;
                accum |= value;
                if (shift >= 8) {
                    shift -= 8;
                    out[index++] = (byte)(accum >> shift & 255);
                }
            }
        }

        return out;
    }

    public static String[] createKey() throws NoSuchAlgorithmException {
        String[] key = new String[3];
        KeyGenerator kg = KeyGenerator.getInstance(enc_dec_algorithm);
        kg.init(168);
        SecretKey secretKey = kg.generateKey();
        KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
        keygen.initialize(1024);
        KeyPair keys = keygen.genKeyPair();
        PublicKey pubkey = keys.getPublic();
        PrivateKey prikey = keys.getPrivate();
        key[0] = base64Encode(secretKey.getEncoded());
        key[1] = base64Encode(pubkey.getEncoded());
        key[2] = base64Encode(prikey.getEncoded());
        return key;
    }

    private static SecretKey getSecretKey(String key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(enc_dec_algorithm);
        return keyFactory.generateSecret(new DESedeKeySpec(base64Decode(key)));
    }

    private static PublicKey getPublicKey(String key) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(new X509EncodedKeySpec(base64Decode(key)));
    }

    private static PrivateKey getPrivateKey(String key) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(base64Decode(key)));
    }

    public static String desEncrypto(String key, String data) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        return desEncrypto(key, data, (String)null);
    }

    public static String desEncrypto(String key, String data, String paramSpec) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        Key secretKey = getSecretKey(key);
        Cipher cipher = Cipher.getInstance(enc_dec_algorithm);
        IvParameterSpec iv = paramSpec == null ? null : new IvParameterSpec(paramSpec.getBytes());
        if (iv != null) {
            cipher.init(1, secretKey, iv);
        } else {
            cipher.init(1, secretKey);
        }

        return base64Encode(cipher.doFinal(data.getBytes()));
    }

    public static byte[] desDecrypto(String key, String data) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
        return desDecrypto(key, data, (String)null);
    }

    public static byte[] desDecrypto(String key, String data, String paramSpec) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
        Key secretKey = getSecretKey(key);
        Cipher cipher = Cipher.getInstance(enc_dec_algorithm);
        IvParameterSpec iv = paramSpec == null ? null : new IvParameterSpec(paramSpec.getBytes());
        if (iv != null) {
            cipher.init(2, secretKey, iv);
        } else {
            cipher.init(2, secretKey);
        }

        return cipher.doFinal(base64Decode(data));
    }

    private static Signature getSigna() throws NoSuchAlgorithmException {
        if (signa == null) {
            signa = Signature.getInstance(sign_algorithm);
        }

        return signa;
    }

    public static boolean isValid(String in, String signdata, String publicKeyStr) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, InvalidKeySpecException {
        boolean flag = false;
        PublicKey publicKey = getPublicKey(publicKeyStr);
        getSigna().initVerify(publicKey);
        getSigna().update(in.getBytes());
        flag = getSigna().verify(base64Decode(signdata));
        return flag;
    }

    public static String sign(String in, String privateKeyStr) throws NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException {
        byte[] signdata = (byte[])null;
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        getSigna().initSign(privateKey);
        getSigna().update(in.getBytes());
        signdata = getSigna().sign();
        return base64Encode(signdata);
    }

    public static void main(String[] args) {
        System.out.println(getMD5Str("502927".getBytes()));
    }

    public static String getMD5Str(byte[] input) {
        StringBuffer sb = new StringBuffer();

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(input);
            byte[] bs = md.digest();

            for(int i = 0; i < bs.length; ++i) {
                int v = bs[i] & 255;
                if (v < 16) {
                    sb.append(0);
                }

                sb.append(Integer.toHexString(v));
            }
        } catch (NoSuchAlgorithmException var6) {
            var6.printStackTrace();
        }

        return sb.toString().toUpperCase();
    }

    @Override
    public String encryptData(String key, String data) {
        return null;
    }

    @Override
    public String decryptData(String key, String data) {
        try {
            return new String(desDecrypto(key,data), Charset.forName("utf-8"));
        } catch (Exception e) {

        }
        return data;
    }
}
